package;

import haxe.io.BytesOutput;
import haxe.io.Output;
import haxe.io.StringInput;
import haxe.Log;
import haxe.PosInfos;
import haxe.Resource;
import haxe.Template;
import format.hxml.Reader;
import format.hxml.Tools;
import format.hxml.Writer;
import neko.FileSystem;
import neko.io.File;
import neko.io.FileInput;
import neko.io.Process;
import neko.Lib;
import neko.Sys;


import Args.Option;
import hscript.Parser.Parser;
import hscript.Interp.Interp;




class Main {
	
	static var DEFAULT_SWF_VERSION = "10";
	
	
	static var T_NEKO = "neko";
	static var T_CPP = "cpp";
	static var T_SWF = "swf";
	static var T_JS = "js";
	static var T_PHP = "php";
	
	static var targetFiles = function () 
	{
		var t = new Hash<String>();

		t.set(T_NEKO, "main.n");
		t.set(T_CPP, "cpp");
		t.set(T_SWF, "main.swf");
		t.set(T_JS, "main.js");
		t.set(T_PHP, "main.php");
		return t;
		
	}();
	
	static var validTargets = [
		Neko, 
		Cpp, 
		Swf,
		Js,
		Php
	];
	
	
	static var compilerOptions = function () 
	{
		var c = new Hash<String>();
		c.set(T_NEKO, "-cmd neko main.n\n" + 
					  "-cmd rm main.n");
		c.set(T_CPP, "");
		c.set(T_SWF, "-swf-header 600:400:31:FFFFFF");
		c.set(T_JS, "");
		c.set(T_PHP, "");
		return c;
		
	}();
	static var targetProjectID = function () 
	{
		var t = new Hash<String>();
		
		t.set(T_NEKO, "12");
		t.set(T_CPP, "14");
		
		t.set(T_SWF, DEFAULT_SWF_VERSION);
		t.set(T_JS, "11");
		t.set(T_PHP, "13");
		return t;
		
	}();
	
	
	
	
	public static function parseListFromString (str : String) : Iterable<String> 
    {

		return str.split(" ").join("").split(",");
		
	}
	public static function validateTargets (a:Iterable<String>):Bool {
		for (i in a) {
			if (!validTarget(i)) return false;
		}
		return true;
		
	}
	
	public static function exitOnError (s:String) {
		Lib.println(s);
		Sys.exit(1);
	}
	
	public static function main ():Void 
	{
		
        
        
        
        
        
		var hxmlData = Tools.stringToHxml(File.getContent("compile.hxml"));
		
		trace(hxmlData);
		
		trace(Tools.hxmlToString(hxmlData));

		Sys.exit(0);
		
		
		var args = Sys.args();
		
		if (Sys.executablePath().lastIndexOf("neko.exe") != -1 && Sys.executablePath().lastIndexOf("neko.exe") == Sys.executablePath().length - 8) {
			args.shift();
		}
		
		
		
		var opt = {
			template : Option.createFlag(["-template"]),
			target : Option.createConv(["-target", "-t"], parseListFromString, validateTargets, ["neko"], false ),
			libs : Option.createConv(["-libs", "-l"], parseListFromString, validateLibs, new Array<String>()),
			mainClass : Option.create(["-main"], "Main"),
			dir : Option.create(["-baseDir"], "."),
			srcDir : Option.create(["-srcDir"], "src"),
			binDir : Option.create(["-binDir"], "bin"),
			libDir : Option.create(["-libDir"], "lib"),
			swfVersion : Option.create(["-swf-version"], DEFAULT_SWF_VERSION ),
			help : Option.createFlag(["--help", "-h"])
		};
		
		//var a = new Args(f);
		
		
		var a = new Args(opt);
		a.parseArgs(Sys.args());
		
		if (opt.help.isSet) {
			printUsage();
			Sys.exit(0);
		}
		
		if (opt.template.isSet) {
			TemplateMaker.create();
			Sys.exit(0);
		}
		
		// defaults
		var targets = opt.target.value;
		var mainClassName = opt.mainClass.value;
		var baseDir = opt.dir.value;
		var libs = opt.libs.value;
		var swfVersion = opt.swfVersion.value;
		var basePath = baseDir + "/";
		
		var libDirAbs = basePath + opt.libDir.value;
		
		var srcDir = opt.srcDir.value;
		var srcDirAbs = basePath + srcDir;
		var srcPathAbs = srcDirAbs + "/";
		
		var binDir = opt.binDir.value;
		var binDirAbs = basePath + binDir;
		
		
		
		if (!FileSystem.exists(baseDir)) {
			FileSystem.createDirectory(baseDir);
		} else {
			if (baseDir == ".") {
				Lib.print("Use current dir as Base Directory (y/n)? ");
				var input = File.stdin().readLine();
				if (input.toLowerCase() != "y") {
					Sys.exit(0);
				} 
			} else 
				throw "directory " + baseDir + " already exists";
		}
		var libDirs = [];
		if (Lambda.count(opt.libs.value) > 0 && opt.libDir.value != null) {
			FileSystem.createDirectory(libDirAbs);
			installLibs(opt.libs.value);
			libDirs = copyLibs(opt.libs.value, libDirAbs);
			
		}
		
		if (!FileSystem.exists(srcDirAbs)) {
			FileSystem.createDirectory(srcDirAbs);
		}
		
		if (!FileSystem.exists(binDirAbs)) {
			FileSystem.createDirectory(binDirAbs);
		}
		
		var libDirsString = "";
		for (i in libDirs) {
			libDirsString += '<class path="' + i + '" />';
		}
		var haxeLibsString = "";
		for (i in libs) {
			haxeLibsString += '<library name="' + i + '" />';
		}
		

		for (t in targets) {
			var template:Template = new Template(Resource.getString("compile"));
			var compileContent = template.execute( { 	target : t, 
														srcDir : srcDir,
														main : mainClassName,
														targetFile : binDir + "/" + getTargetFile(t), 
														addOn : getAddOn(t), 
														libs : getLibsString(opt.libs.value)});
			
			File.write(basePath + "compile_" + t + ".hxml", false).writeString(compileContent);
			
			var template:Template = new Template(Resource.getString("project"));
			var compileContent = template.execute({
													targetID : getTargetID(t, swfVersion), 
													libDirs : libDirsString, 
													haxeLibs : haxeLibsString, 
													compileTarget : '<compile path="compile_' + t + '.hxml" />', 
													buildCommand : "haxe compile_" + t + ".hxml", 
													srcDir : '<class path="' + srcDir + '" />' 
												 });
			File.write(basePath + "project_" + t + ".hxproj", false).writeString(compileContent);
		}
		
		File.write(srcPathAbs + mainClassName + ".hx", false).writeString(Resource.getString("main"));
		
	}
	
	static private function printUsage():Void
	{
		Lib.println(Resource.getString("help"));
	}
	
	static private function getTargetID(t:String, swfVersion:String):String
	{
		if (t == T_SWF) {
			return swfVersion;
		} else {
			return targetProjectID.get(t);
		}
	}
	
	static private function copyLibs(libs:Iterable<String>, destDir:String):Array<String>
	{
		var libDirs = [];
		for (l in libs) {
			var output = runCommandWithOutput("haxelib", ["path", l]);
			var chunks = output.split("\n");
			for (i in 0...chunks.length) {
				if (chunks[i].indexOf("-L") == 0) {
					chunks[i] = "";
				}
			}
			output = chunks.join("");
			var srcDir = output.split("\n").join("").split("\r").join("").split("\\").join("/");
			
			if (StringTools.endsWith(srcDir, "/")) {
				srcDir = srcDir.substr(0, srcDir.length -1);
			}
			
			var targetDir = destDir + "/" + l;
			libDirs.push(targetDir);
			FileSystem.createDirectory(targetDir);
			
			trace("copy from: " + srcDir);
			trace("copy to: " + targetDir + "/" + l);
			
			FileTools.copyDirectoryRecursive(srcDir, targetDir, function (s) return StringTools.endsWith(s, ".hx"));
		}
		return libDirs;
	}
	
	
	public static function runCommandWithOutput( cmd : String, ?args : Array<String> ) : String {
		
        
        
        
        var tmpFile = "____tmp_____" + Date.now().toString().split(" ").join("").split(":").join("_");
		
		args.push(">" + tmpFile);
		var res = Sys.command(cmd, args);
		var content = File.getContent(tmpFile);
		FileSystem.deleteFile(tmpFile);
		return content;
	}
	
	static private function validateLibs(libs:Iterable<String>):Bool
	{
		for (l in libs) {
			var searchOnDisk = runCommandWithOutput("haxelib", ["path", l]);
			
			if (searchOnDisk.indexOf("Project " + l + " is not installed") != -1) {
				
				var res = runCommandWithOutput("haxelib", ["search", l]);
				if (res.indexOf("\n1 projects found") == -1) {
					trace("Library " + l + " is not available on Disk nor through haxelib");
					return false;
				}
			}
		}
		return true;
	}
	
	static private function installLibs(libs:Iterable<String>):Void
	{
		
		
		for (l in libs) {
			var searchOnDisk = runCommandWithOutput("haxelib", ["path", l]);
			
			if (searchOnDisk.indexOf("Project " + l + " is not installed") != -1) {
				Sys.command("haxelib", ["install", l]);
				
			}
			
		}
	}
	
	static private function getLibsString(libs:Iterable<String>):String
	{
		var str = "";
		
		for (l in libs) {
			str += "-lib " + l + "\n";
		}
		return str;
	}
	
	public static function validTarget (target:String):Bool  {
		return Lambda.has(validTargets, target);
	}
	
	public static function getTargetFile (target:String):String 
	{
		return targetFiles.get(target);
		
	}
	public static function getAddOn (target:String):String 
	{
		return compilerOptions.get(target);
	}
}